#include "GLProgram.h"
#include <istream>

GLint getFileLength(ifstream& file)
{
	if(!file.good()) return 0;

	unsigned long pos=file.tellg();
	file.seekg(0,ios::end);
	GLint len = file.tellg();
	file.seekg(ios::beg);

	return len;
}

HRESULT CGLProgram::LoadShaderProgramFromFile(char* FileName, GLchar** ShaderSource, GLint& len)
{

	ifstream file;
	file.open(FileName, ios::in); // opens as ASCII!
	if(!file) return E_INVALIDARG;

	len = getFileLength(file);

	if (len==0) return E_INVALIDARG;   // Error: Empty File 

	*ShaderSource = (GLchar*) new char[len+1];
	if (!*ShaderSource) return E_OUTOFMEMORY;   // can't reserve memory

	// len isn't always strlen cause some characters are stripped in ascii read...
	// it is important to 0-terminate the real length later, len is just max possible value... 
	(*ShaderSource)[len] = 0; 

	UINT i=0;
	while (file.good())
	{
		(*ShaderSource)[i] = file.get();       // get character from file.
		if (!file.eof())
			i++;
	}

	(*ShaderSource)[i] = 0;  // 0-terminate it at the correct position

	file.close();

	return S_OK; // No Error
}

template<DWORD ShaderType>
GLuint& CGLProgram::GetShaderObject()
{
	switch(ShaderType)
	{
	case GL_VERTEX_SHADER:
		return m_VS;
	case GL_FRAGMENT_SHADER:
		return m_FS;
	default:
		VISIO_ASSERT(0);
	}
}

// common method for creating a shader
template<DWORD ShaderType>
HRESULT CGLProgram::CreateShaderProgramCommon(char* fileName)
{
	GLchar* ShaderSource;
	GLint len = 0;
	GLint success;
	HRESULT res = LoadShaderProgramFromFile(fileName, &ShaderSource, len);
	
	if (CHECK_RES(res))
	{
		// compiling the shader
		GLuint& ShaderObj =  GetShaderObject<ShaderType>();
		ShaderObj = glCreateShader(ShaderType);
		glShaderSource(ShaderObj, 1, (const GLchar**)&ShaderSource, &len);
		glCompileShader(ShaderObj);
		// checking for compilation errors
		glGetShaderiv(ShaderObj, GL_COMPILE_STATUS, &success);
		if (!success) {
			GLchar InfoLog[1024];
			glGetShaderInfoLog(ShaderObj, sizeof(InfoLog), NULL, InfoLog);
			CONSOLE("Error compiling shader type %d: '%s'\n", ShaderType, InfoLog);
			VISIO_ASSERT(0);
			res = E_FAIL;
		}
	}

	return res;

}
HRESULT CGLProgram::CreateVertexShader(char* vsShaderFileName)
{
	return CreateShaderProgramCommon<GL_VERTEX_SHADER>(vsShaderFileName);
}

HRESULT CGLProgram::CreateFragmentShader(char* fragShaderFileName)
{
	return CreateShaderProgramCommon<GL_FRAGMENT_SHADER>(fragShaderFileName);
}

CGLProgram::CGLProgram(char* vsShaderFileName, char* fragShaderFileName)
{
	HRESULT vsCreationResult, fsCreationResult;
	GLint res = 0;

	m_ShaderProgram = glCreateProgram();

	if (vsShaderFileName)
	{
		vsCreationResult = CreateVertexShader(vsShaderFileName);
		VISIO_ASSERT(vsCreationResult == S_OK);
		if (vsCreationResult == S_OK)
		{
			glAttachShader(m_ShaderProgram, m_VS);
		}
		
	}

	if (fragShaderFileName)
	{
		fsCreationResult = CreateFragmentShader(fragShaderFileName);
		VISIO_ASSERT(fsCreationResult == S_OK);
		if (fsCreationResult == S_OK)
		{
			glAttachShader(m_ShaderProgram, m_FS);
		}
		
	}

	glLinkProgram(m_ShaderProgram);

	
	glGetProgramiv(m_ShaderProgram, GL_LINK_STATUS, &res);
	if (res == 0) {
		GLchar ErrorLog[1024];
		glGetProgramInfoLog(m_ShaderProgram, sizeof(ErrorLog), NULL, ErrorLog);
		CONSOLE("Error linking shader program: '%s'\n", ErrorLog);
	}
	
}